#include "NeuralLayer.h"

#include "BasicDebug.h"

#include <QtCore/QString>
#include <assert.h>
#include <iostream>
namespace jcrada {

    NeuralLayer::NeuralLayer( int number_of_neurons ) :
    _number_of_neurons( number_of_neurons ),
    _neurons( new Neuron*[number_of_neurons] ) {
        for (int i = 0; i < getNumberOfNeurons(); ++i) {
            setNeuron(i, NULL);
        }
    }

    NeuralLayer::NeuralLayer( int number_of_neurons, int neuron_input_size ) :
    _number_of_neurons( number_of_neurons ),
    _neurons( new Neuron*[number_of_neurons] ) {
        for (int i = 0; i < getNumberOfNeurons(); ++i) {
            _neurons[i] = new Neuron(neuron_input_size);
        }
    }

    NeuralLayer::~NeuralLayer( ) {
        for (int i = 0; i < getNumberOfNeurons(); ++i) {
            if (_neurons[i]) {
                delete _neurons[i];
            }
        }
        delete [] _neurons;
    }

    NeuralLayer* NeuralLayer::clone( ) const {
        NeuralLayer* result = new NeuralLayer(getNumberOfNeurons());
        for (int i = 0; i < result->getNumberOfNeurons(); ++i) {
            result->setNeuron(i, getNeuron(i).clone());
        }
        return result;
    }

    void NeuralLayer::setActivationFunction( Neuron::eActivationFunction af ) {
        for (int i = 0; i < getNumberOfNeurons(); ++i) {
            getNeuron(i).setActivationFunction(af);
        }
    }

    NeuralLayer* NeuralLayer::fromXml( QDomElement& xml, bool* ok ) {
        int number_of_neurons = xml.attribute("number-of-neurons", "0").toInt(ok);
        NeuralLayer* result = new NeuralLayer(number_of_neurons);

        QDomNode child = xml.firstChild();
        int i = 0;
        while (!child.isNull()) {
            QDomElement e = child.toElement();
            if (e.tagName() == "Neuron") {
                assert(i < number_of_neurons);
                result->setNeuron(i++, Neuron::fromXml(e, ok));
            }
            child = child.nextSibling();
        }
        if (!*ok) {
            BDEBUG("Error in NeuralLayer::fromXml");
        }
        return result;
    }

    void NeuralLayer::toXml( QDomDocument& xml, QDomElement* parent ) const {
        QDomElement layer = xml.createElement("NeuralLayer");
        layer.setAttribute("number-of-neurons", getNumberOfNeurons());
        for (int i = 0; i < getNumberOfNeurons(); ++i) {
            getNeuron(i).toXml(xml, &layer);
        }
        if (parent) {
            parent->appendChild(layer);
        } else {
            xml.appendChild(layer);
        }
    }

    int NeuralLayer::main( int argc, char** argv ) {
        (void) argc;
        (void) argv;
        NeuralLayer layer(5, 3);
        for (int i = 0; i < layer.getNumberOfNeurons(); ++i) {
            layer.getNeuron(i).setInput(i, i + 1);
            layer.getNeuron(i).setWeight(i, (i + 1) * 2);
            layer.getNeuron(i).setThreshold(i);
            layer.getNeuron(i).setActivationFunction(Neuron::AF_TANH);
        }
        QDomDocument doc;
        layer.toXml(doc);
        QString xml = doc.toString();
        BDEBUG(xml.toStdString());

        doc.clear();
        {
            QString* error_msg = new QString();
            int *error_line = new int(), *error_col = new int();
            doc.setContent(xml, error_msg, error_line, error_col);
            std::cout << "Error: " << error_msg->toStdString() << " at [" << *error_line << " , "
                    << *error_col << "]" << std::endl;
            delete error_msg;
            delete error_line;
            delete error_col;
        }
        QDomElement root = doc.firstChild().toElement();
        NeuralLayer nl(8);

        doc.clear();
        nl.toXml(doc);

        xml = doc.toString();
        std::cout << xml.toStdString() << std::endl;
        return 0;
    }

}
